package org.ndx.jsg.input;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.ndx.jsg.FileInfos;
import org.ndx.jsg.util.MetadataLoader;
import org.xml.sax.SAXException;

import com.google.inject.Inject;

public class DefaultFileInfosProvider implements FileInfosProvider {
	private static final Logger logger = Logger
			.getLogger(DefaultFileInfosProvider.class.getName());

	private MetadataLoader metadataLoader = new MetadataLoader();
	/**
	 * collection of file infos builders usable
	 */
	@Inject private Set<FileInfosBuilder> builders;
	
	public DefaultFileInfosProvider() {
		
	}
	
	public DefaultFileInfosProvider(FileInfosBuilder ...used) {
		builders = new HashSet<FileInfosBuilder>();
		for(FileInfosBuilder b : used) {
			builders.add(b);
		}
	}

	@Override
	public Collection<FileInfos> getFiles(File defaultRoot, String[] inputPaths, Collection<String> ignoredNames) {
		Collection<FileInfos> returned = new LinkedList<FileInfos>();
		for (String input : inputPaths) {
			File inputFile = new File(input);
			if(logger.isLoggable(Level.INFO))
				logger.log(Level.INFO, "checking input path "+inputFile.getPath());
			if (inputFile.exists()) {
				returned.addAll(getFiles(inputFile, "", ignoredNames));
			} else {
				inputFile = new File(defaultRoot + File.separator + input);
				if (inputFile.exists()) {
					returned.addAll(getFiles(inputFile, "", ignoredNames));
				} else {
					logger
							.log(
									Level.WARNING,
									"unable to resolve %s to an existing path either as an absolute one or as a relative one (%s), it will be ignored",
									new String[] { input,
											inputFile.getAbsolutePath() });
				}
			}
		}
		return returned;
	}

	/**
	 * Recursively scans file from input file
	 * 
	 * @param inputFile
	 * @return
	 */
	private Collection<? extends FileInfos> getFiles(File inputFile, String relativePath, final Collection<String> ignoredNames) {
		Collection<FileInfos> returned = new LinkedList<FileInfos>();
		File[] matching = inputFile.listFiles(new FilenameFilter() {
			
			@Override
			public boolean accept(File dir, String name) {
				return !ignoredNames.contains(name);
			}
		});
		for (File f : matching) {
			String fileRelativePath = relativePath+File.separator+f.getName();
			if (f.isDirectory()) {
				if(logger.isLoggable(Level.FINE))
					logger.log(Level.FINE, "checking directory "+ f.getAbsolutePath());
				returned.addAll(getFiles(f, fileRelativePath, ignoredNames));
			} else {
				if(logger.isLoggable(Level.FINE))
					logger.log(Level.FINE, "checking file "+ f.getAbsolutePath());
				try {
					returned.add(getFileInfos(f, fileRelativePath));
				} catch (Exception e) {
					logger.log(Level.FINE, "unable to build infos for "
							+ f.getAbsolutePath(), e);
				}
			}
		}
		return returned;
	}

	private FileInfos getFileInfos(File f, String relativePath) throws Exception {
		Metadata metadata = metadataLoader.getMetadata(f);
		for (FileInfosBuilder b : builders) {
			FileInfos returned = b.getFileInfo(f, relativePath, metadata);
			if (returned != null)
				return returned;
		}
		throw new Exception("no builder for file " + f.getAbsolutePath()
				+ " considered as " + metadata.get(Metadata.CONTENT_TYPE));
	}

}
